/**
 * Copyright 2007 Jason Horman, Pete Aykroyd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.functor;

import java.io.Serializable;

/**
 * A org.functor that takes two arguments and returns a value.
 */
public abstract class BinaryFunction<Left,Right,Return> implements Serializable {
	/**
	 * Evaluate this function.
	 *
	 * @param left  the first element of the ordered pair of arguments
	 * @param right the second element of the ordered pair of arguments
	 * @return the result of this function for the given arguments
	 */
	public abstract Return evaluate(Left left, Right right);

	public Function bind(final Left left, final Right right) {
		final BinaryFunction<Left,Right,Return> me = this;
		return new Function() {
			public Return evaluate() {
				return me.evaluate(left, right);
			}
		};
	}

	public UnaryFunction bindLeft(final Object left) {
		final BinaryFunction me = this;
		return new UnaryFunction() {
			public Object evaluate(Object obj) {
				return me.evaluate(left, obj);
			}
		};
	}

	public UnaryFunction<Left,Return> bindRight(final Right right) {
		final BinaryFunction<Left,Right,Return> me = this;
		return new UnaryFunction<Left,Return>() {
			public Return evaluate(Left left) {
				return me.evaluate(left, right);
			}
		};
	}

	/**
	 * When run executes the UnaryFunction passed in with the right hand argument
	 * to the binary function.
	 */
	public static BinaryFunction dropLeft(final UnaryFunction andExecute) {
		return new BinaryFunction() {
			public Object evaluate(Object left, Object right) {
				return andExecute.evaluate(right);
			}
		};
	}

	/**
	 * When run executes the UnaryFunction passed in with the left hand argument
	 * to the binary function.
	 */
	public static BinaryFunction dropRight(final UnaryFunction andExecute) {
		return new BinaryFunction() {
			public Object evaluate(Object left, Object right) {
				return andExecute.evaluate(left);
			}
		};
	}

	/**
	 * When run executes the Function passed with no arguments.
	 */
	public static BinaryFunction drop(final Function andExecute) {
		return new BinaryFunction() {
			public Object evaluate(Object left, Object right) {
				return andExecute.evaluate();
			}
		};
	}

	/**
	 * Constructs a BinaryFunction that always returns the same value.
	 */
	public static BinaryFunction constant(final Object value) {
		return new BinaryFunction() {
			public Object evaluate(Object left, Object right) {
				return value;
			}
		};
	}

	public static final BinaryFunction NULL = constant(null);
}
